#include <ctype.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "vcd-common.h"
#include "vcd-ping.h"

#ifdef __WIN32__
#include <tlhelp32.h>
#endif

#ifdef __linux__
#include <netdb.h>
#endif

#include "vcd-conf/vcd-conf.h"
#include "vcd-log.h"
#include "vcd-util.h"
#include "vcd-conf/vcd-conf.h"
#include "vcd-download.h"

const char* key = "verycloudveryclo";//aes加解密秘钥

#define AES_BITS 10240
#define MSG_LEN 10240

StNetWindowCtx g_net_window_ctx;

static char* g_img_path;

void vcd_common_set_img_path(const char* path){
    g_img_path = path;
}
char* vcd_common_get_absolute_img_path(const char* name){
    if(!g_img_path) return NULL;

    char icon_path[1000] = {0};
    sprintf(icon_path,"%s/%s",g_img_path,name);

    // 未做内存释放
    return g_canonicalize_filename(icon_path,NULL);
}

void set_css(GtkWidget *widget, const gchar *data_css)
{
    GtkCssProvider *provider = gtk_css_provider_new ();
    GtkStyleContext *context = gtk_widget_get_style_context(widget);
    gtk_css_provider_load_from_data(provider, data_css, -1, NULL);
    gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
    g_object_unref(provider);
}

void set_img(GtkWidget *win, const char *widget_class, const char *pic_name)
{
    char css_string[500];
    char path[100];
    if(g_img_path)
        snprintf(path, sizeof(path),"%s/%s",g_img_path, pic_name);
    else
        snprintf(path, sizeof(path), VCD_PIC_PATH"%s", pic_name);

    snprintf(css_string,sizeof(css_string), "%s{background-image:url('%s')}", widget_class, path);
    set_css(win, css_string);
}

gboolean is_alnum_dot(char *str)
{
    int i = 0;
    for (i = 0; i < strlen(str); i++)
    {
        if ((str[i] <= '9' && str[i] >= '0') || (str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z') || str[i] == '.')
        {
        } else
        {
            return FALSE;
        }
    }
    return TRUE;
}

gboolean is_num_dot(char *str)
{
    if (strlen(str) == 0)
    {
        return FALSE;
    }
    int i = 0;
    for (i = 0; i < strlen(str); i++)
    {
        if ((str[i] <= '9' && str[i] >= '0') || str[i] == '.')
        {
        } else
        {
            return FALSE;
        }
    }
    return TRUE;
}

gboolean is_num_dot_colon(char *str)
{
    int i = 0;
    for (i = 0; i < strlen(str); i++)
    {
        if ((str[i] <= '9' && str[i] >= '0') || str[i] == '.' || str[i] == ':')
        {
        } else
        {
            return FALSE;
        }
    }
    return TRUE;
}

gboolean is_digit(const char *str)
{
    int i = 0;
    for (i = 0; i < strlen(str); i++)
    {
        if (str[i] <= '9' && str[i] >= '0')
        {
        } else
        {
            return FALSE;
        }
    }
    return TRUE;
}

gboolean ends_with(char const *str, char const *suffix)
{
    int strlength = strlen(str);
    int suflength = strlen(suffix);
    if (strlength < suflength)
    {
        return FALSE;
    }
    if (strcmp(str + strlength - suflength, suffix) == 0)
    {
        return TRUE;
    } else
    {
        return FALSE;
    }
}

gboolean has_char(char const *str, char c)
{
    if (c == 0)
    {
        return FALSE;
    }
    int i;
    for (i = 0; i < strlen(str); i++)
    {
        if (str[i] == c)
        {
            return TRUE;
        }
    }
    return FALSE;
}

int getlen(char *result) {
    int i = 0;
    while (result[i] != '\0') {
        i++;
    }
    return i;
}


//去除首部空格
char *ltrim(char *str)
{
	if (str == NULL || *str == '\0')
	{
		return str;
	}
 
	int len = 0;
	char *p = str;
	while (*p != '\0' && isspace(*p))
	{
		++p;
		++len;
	}
 
	memmove(str, p, strlen(str) - len + 1);
 
	return str;
}


/**********************************************************
函数名：aes_encrypt
参数：const char* str_in        --输入字符
参数：unsigned char* key        --key
参数：unsigned char* out        --输出字符
返回值:int                      --0失败  1成功
说明：加密
***********************************************************/
int aes_encrypt(char* str_in, char* out)
{
    //加密的初始化向量
    unsigned char iv[AES_BLOCK_SIZE];

    //16位密码
    for (int i = 0; i < 16; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        LOG_ERROR("AES_set_encrypt_key error");
        return 0;
    }

    int len = getlen(str_in);

    AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
    return 1;
}

/**********************************************************
函数名：aes_decrypt
参数：const char* str_in        --输入
参数：unsigned char* key        --key
参数：unsigned char* out        --输出
返回值：int            　　　　　 --0失败  1成功
说明：　　　　　　　　　　　　　　　 --解密
***********************************************************/
int aes_decrypt(char* str_in, char* out)
{
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
    for (int i = 0; i < 16; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        LOG_ERROR("AES_set_decrypt_key error");
        return 0;
    }

    int len = getlen(str_in);

    AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    return 1;
}

/**********************************************************
函数名：aes_encrypt
参数：const char* str_in        --输入字符
参数：unsigned char* key        --key
参数：unsigned char* out        --输出字符
返回值:int                      --0失败  1成功
说明：加密
***********************************************************/
int aes_encrypt_extend(char* str_in,int len, char* out)
{
    //加密的初始化向量
    unsigned char iv[AES_BLOCK_SIZE];

    //16位密码
    for (int i = 0; i < 16; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }

    AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
    return 1;
}
/**********************************************************
函数名：aes_decrypt
参数：const char* str_in        --输入
参数：unsigned char* key        --key
参数：unsigned char* out        --输出
返回值：int            　　　　　 --0失败  1成功
说明：　　　　　　　　　　　　　　　 --解密
***********************************************************/
int aes_decrypt_extend(char* str_in, int len,char* out)
{
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
    for (int i = 0; i < 16; ++i)
        iv[i] = 0;

    AES_KEY aes;
    if (AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }

    AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    return 1;
}

int base64_encode(char *in_str, int in_len, char *out_str)
{
    BIO *b64, *bio;
    BUF_MEM *bptr = NULL;
    size_t size = 0;

    if (in_str == NULL || out_str == NULL)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_write(bio, in_str, in_len);
    BIO_flush(bio);

    BIO_get_mem_ptr(bio, &bptr);
    memcpy(out_str, bptr->data, bptr->length);
    out_str[bptr->length] = '\0';
    size = bptr->length;

    BIO_free_all(bio);
    return size;
}

int base64_decode(char *in_str, int in_len, char *out_str)
{
    BIO *b64, *bio;

    int size = 0;

    if (in_str == NULL || out_str == NULL)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

    bio = BIO_new_mem_buf(in_str, in_len);
    bio = BIO_push(b64, bio);

    size = BIO_read(bio, out_str, in_len);
    out_str[size] = '\0';

    BIO_free_all(bio);
    return size;
}

char *strlower(char *str)
{
    char *p = str;
    while(*p){
        *p = tolower(*p);
        ++p;
    }
    return str;
}
char *strupper(char *str)
{
    char *p = str;
    while(*p){
        *p = toupper(*p);
        ++p;
    }
    return str;
}

char* exe_command(const char* cmd, size_t* len)
{
    if (cmd == NULL)
        return NULL;

    FILE* pf = popen(cmd, "r");
    if (pf == NULL)
        return NULL;

    char buf[4096];
    char* s = NULL;
    size_t total= 0;
    while (!feof(pf)) {
        memset(buf, 0, sizeof(buf));
        char* t = fgets(buf, sizeof(buf), pf);
        if (t == NULL)
            break;
        
        size_t si = strlen(t);
        if (s) {
            char* ptr = (char*)realloc(s, si + total);
            if (ptr == NULL) {
                free(s);
                s = NULL;
                return NULL;
            }
            s = ptr;
        } else {
            s = (char*)malloc( si);
            if (s == NULL)
                return NULL;
        }

        memcpy(s + total, buf, si);
        total += si;
    }
    pclose(pf);
    pf = NULL;

    if (s == NULL)
        return NULL;

    if (total > 0) {
        s[total -1]=0;//last is '\n'
        if(len)
            *len = total-1;
    }
    return s;
}


uint64_t get_time(void)
{
    struct timeval t;
    int res = gettimeofday(&t, NULL);
    if(res != 0){
        return 0;
    }

    return t.tv_sec * 1000000ULL + t.tv_usec;
}


void sort_vm_info(vm_info *f, gint count)
{
    if(f == NULL || count <= 1){
        return;
    }

    int res = 0;
    gint i = 0,j = 0;
    for(i = 0; i < count - 1; ++i){
        for(j = 0; j < count - 1 -i; ++j){
            res = strncmp(f[j].name, f[j + 1].name, sizeof(f[j].name));
            if(res > 0){
                vm_info tmp;
                memcpy(&tmp, f + j, sizeof(vm_info));
                memcpy(f + j, f + j + 1, sizeof(vm_info));
                memcpy(f + j + 1 , &tmp, sizeof(vm_info));
            }
        }
    }
}

void vm_config_response_free(vm_config_response* resp)
{
    if(resp == NULL){
        return;
    }

    SAFE_FREE(resp->args);
    SAFE_FREE(resp->name);
    SAFE_FREE(resp->net0);
    SAFE_FREE(resp->ostype);
}

void vcd_common_set_app_icon(GtkWidget* win,const char *name){
    GError *error = NULL;
    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(name, &error);
    if (error && !pixbuf) {
        LOG_ERROR(error->message);
        g_error_free(error);
    }else{
        gtk_window_set_icon(GTK_WINDOW(win),pixbuf);
    }
}

static gboolean  net_window_timer_callback(gpointer user_data){
    GtkLabel* label = (GtkLabel*)user_data;

    gchar text[500] = {0},tmp[50] = {0};

    // 第一行,用于固定行长度
    strcat(text,"                                     \n");

    // 添加帧率信息
    if(0){
    //g_net_window_ctx.frame_rate = rand()%75;
    sprintf(tmp,"帧 率: %d\n",g_net_window_ctx.frame_rate);
    strcat(text,tmp);
    }

    // 添加网络延时信息
    if(1){
    //g_net_window_ctx.network_delay = rand()%1000;
    memset(tmp,0,sizeof (tmp));
    if(g_net_window_ctx.network_delay > 0)
        sprintf(tmp,"网络延时: %.2f ms  \n",g_net_window_ctx.network_delay);
    else
        sprintf(tmp,"网络延时: < 1 ms  \n");
    strcat(text,tmp);
    }

    // 添加网络丢包信息
    if(1){
    //g_net_window_ctx.network_pkt_loss_rate = rand()%20;
    memset(tmp,0,sizeof (tmp));
    if(g_net_window_ctx.network_pkt_loss_rate > 0)
        sprintf(tmp,"网络丢包: %.0f %%  \n",g_net_window_ctx.network_pkt_loss_rate);
    else
        sprintf(tmp,"网络丢包: < 1 %%  \n");
    strcat(text,tmp);
    }

    // 添加分辨率信息
    if(1){
    //g_net_window_ctx.screen_width = 1920;
    //g_net_window_ctx.screen_height = 1080;
    memset(tmp,0,sizeof (tmp));
    sprintf(tmp,"分辨率: %d x %d  \n",g_net_window_ctx.screen_width,g_net_window_ctx.screen_height);
    strcat(text,tmp);
    }

    gtk_label_set_text(label,text);

    return TRUE;
}

static void vcd_ping_callback(StVcdPingVar *data) {

    StVcdPingVar *ctx = (StVcdPingVar *)data;
    M_DEBUG("vcd_ping_callback delay:%d,loss:%d", ctx->delay, ctx->pkt_loss_rate);

    g_net_window_ctx.network_delay = ctx->delay;
    g_net_window_ctx.network_pkt_loss_rate = ctx->pkt_loss_rate;
    //free(ctx);
}

static void net_window_show_event(GtkWidget* widget,gpointer data){

    vcd_ping_start(vcd_ping_callback, NULL);

    //timer
    if(!g_net_window_ctx.timer){
        g_net_window_ctx.timer = g_timeout_add_seconds(1,(GSourceFunc)net_window_timer_callback, data);
    }

}

static void net_window_hide_event(GtkWidget* widget,gpointer data){

    vcd_ping_stop();

    //timer
    if(g_net_window_ctx.timer){
        g_source_remove(g_net_window_ctx.timer);
        g_net_window_ctx.timer = 0;
    }
}

static gboolean net_window_destroy_event(GtkWidget* widget,GdkEvent* event,gpointer data){

    net_window_hide_event(widget,data);
}

GtkWidget* vcd_net_window_new(GtkWidget *overlay){

    GtkWidget *widget = gtk_label_new("");

    gtk_label_set_justify(GTK_LABEL(widget),GTK_JUSTIFY_LEFT);
    // background-color:#00FF00
    set_css(widget, "label{text-align:left;font-size:xx-large;color:#FF0000}");

    g_signal_connect(widget,"show",net_window_show_event,widget);
    g_signal_connect(widget,"hide",net_window_hide_event,widget);
    g_signal_connect(widget,"destroy",net_window_destroy_event,widget);


    //GTK_ALIGN_BASELINE GTK_ALIGN_CENTER GTK_ALIGN_START GTK_ALIGN_END
    gtk_widget_set_halign(widget, GTK_ALIGN_END);
    gtk_widget_set_valign(widget, GTK_ALIGN_START);
    gtk_widget_set_margin_top(widget,30);
    //gtk_widget_set_margin_right(widget,60);
    gtk_overlay_add_overlay(GTK_CONTAINER(overlay), widget);

    gtk_overlay_set_overlay_pass_through(GTK_CONTAINER(overlay),widget,TRUE);
    return widget;
}

void vcd_net_window_set_screen_size(int width,int height){
    g_net_window_ctx.screen_width = width;
    g_net_window_ctx.screen_height = height;
}

/**
 * @brief check_is_process_exist
 * @param process_name
 * @param is_kill_it
 * @return true: if process exist    false:not exist
 */
gboolean check_is_process_exist(char *process_name, gboolean is_kill_it)
{
    gboolean bFind = FALSE;
#ifdef __WIN32__
    HANDLE hProcessSnap;
    HANDLE hProcess;
    PROCESSENTRY32 pe32;
    DWORD dwPriorityClass;

    // Take a snapshot of all processes in the system.
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }

    // Set the size of the structure before using it.
    pe32.dwSize = sizeof(PROCESSENTRY32);

    // Retrieve information about the first process,
    // and exit if unsuccessful
    if (!Process32First(hProcessSnap, &pe32))
    {
        CloseHandle(hProcessSnap);          // clean the snapshot object
        return FALSE;
    }

    char szPeProcName[_MAX_PATH] = {0};

    // Now walk the snapshot of processes, and
    // display information about each process in turn
    do
    {
        // Retrieve the priority class.
        sprintf(szPeProcName, "%s", pe32.szExeFile );

        //pe32.th32ProcessID
        if (0 == strcmp(szPeProcName, process_name))
        {
            int currProcessId = getpid();
            if (currProcessId != pe32.th32ProcessID) {

                bFind = TRUE;

                if (is_kill_it) {
                    //加了下面代码，Vclient无法退出                
                    M_DEBUG("ready to kill process:%s", szPeProcName);
                    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
                    TerminateProcess(hProcess,0);  //杀死进程
                    CloseHandle(hProcess);

                    char str_cmd[100] = {0};
                    sprintf(str_cmd, "/PID %d", pe32.th32ProcessID);
                    ShellExecute(NULL, "open", "taskkill.exe", str_cmd, "", SW_HIDE);
                }

                break;
            }
        }
    } while (Process32Next(hProcessSnap, &pe32));

    CloseHandle(hProcessSnap);

#endif
    return bFind;
}

void vcd_common_url_decode(char* url)
{
    // Replace (in place) any %<hex><hex> sequences with the appropriate 8-bit character.
    char* cursor = url;
    while (*cursor) {
        if ((cursor[0] == '%') &&
             cursor[1] && isxdigit(cursor[1]) &&
             cursor[2] && isxdigit(cursor[2])) {
            // We saw a % followed by 2 hex digits, so we copy the literal hex value into the URL, then advance the cursor past it:
            char hex[3];
            hex[0] = cursor[1];
            hex[1] = cursor[2];
            hex[2] = '\0';
            *url++ = (char)strtol(hex, NULL, 16);
            cursor += 3;
        } else {
            // Common case: This is a normal character or a bogus % expression, so just copy it
            *url++ = *cursor++;
        }
    }
    *url = '\0';
}

static gboolean isDoMainName(const char* str){
    if(str==0) return FALSE;

    int len = strlen(str);
    for(int i=0;i<len;i++){
        if((str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z')){
            return TRUE;
        }
    }
    return FALSE;
}

char* vcd_common_resolve_domain(const char* domain){
    if(!isDoMainName(domain)) return domain;

    struct hostent	*addr = gethostbyname(domain);
    if(addr){

        for(int i = 0;i<addr->h_length;i++){
            struct sockaddr_in sa = {0};
            memcpy(&sa.sin_addr.s_addr,addr->h_addr_list[i],addr->h_length);
            char* ip = inet_ntoa(sa.sin_addr);
            return ip;
        }
    }
    return domain;
}

void vcd_common_init_platform(){
#ifdef __WIN32__
    WSADATA wsadata;
    if(WSAStartup(MAKEWORD(1,1),&wsadata)!=0)
    {
       printf("wsastartup() failed!!\n");
    }
#endif

}

void vcd_common_init_some_path(const char* arg0,spice_connect_data* scd){

    const char* vclient_path = arg0;
    const char* dir_name = g_path_get_dirname(vclient_path);
    //M_DEBUG("dir_name:%s",dir_name);

    const char* stream_path = g_strconcat(dir_name,"/","verystream.exe",NULL);
    char* new_path    = g_canonicalize_filename(stream_path,NULL);
    //M_DEBUG("stream_path:%s,new_path:%s",stream_path,new_path);

    const char* log4c_path = g_strconcat(dir_name,"/../etc/","log4crc",NULL);
    char* new_log4c_path   = g_canonicalize_filename(log4c_path,NULL);
    //M_DEBUG("log4c_path:%s,new_log4c_path:%s",log4c_path,new_log4c_path);

    const char* log_dir = g_strconcat(dir_name,"/../log/",NULL);
    char* new_log_dir  = g_canonicalize_filename(log_dir,NULL);
    //M_DEBUG("log_dir:%s,new_log_dir:%s",log_dir,new_log_dir);

    const char* install_path = g_strconcat(dir_name,"/..",NULL);
    char* new_install_path  = g_canonicalize_filename(install_path,NULL);
    //M_DEBUG("install_path:%s,new_install_path:%s",install_path,new_install_path);

    const char* theme_path = g_strconcat(dir_name,"/../share/icons",NULL);
    char* new_theme_path  = g_canonicalize_filename(theme_path,NULL);

    // 设置全局 img 路径
    // set_css() 中用
    const char* img_path = g_strconcat(dir_name,"/../share/img",NULL);
    char* new_img_path  = g_canonicalize_filename(img_path,NULL);
    vcd_common_set_img_path(new_img_path);

    // 未做内存释放
    scd->rdp_stream_path = new_path;
    scd->log4crc_path    = new_log4c_path;
    scd->log_dir         = new_log_dir;
    scd->install_path    = new_install_path;
    scd->theme_path      = new_theme_path;
}

void update_spice_info_sync(spice_connect_data *scd){

    M_INFO("[reconnect] begin reconnect");

    struct vcd_conf* tmp_vcd_conf = g_vcd_conf;

    // if not call 'read_vcd_conf()',do nothing
    if(!tmp_vcd_conf){
        M_INFO("[reconnect] g_vcd_conf == null, stop reconnect");
        return;
    }

    // if not open usb and audio,do nothing
    if(!scd->use_audio && !scd->use_usb_redir){
        M_INFO("[reconnect] use_audio==false and use_usb_redir==false, stop reconnect");
        return;
    }

#ifdef __WIN32__
    M_DEBUG("windows can not get username and passwd, can not make spice reconnect");
    return;
#endif

    //tell_gateway_client_disconnect();

    char* host      = tmp_vcd_conf->ip;
    char* user_name = tmp_vcd_conf->username;
    char* user_pass = tmp_vcd_conf->passwd;
    char* node      = scd->vmnode;
    int   vmid      = scd->vmid;
    //password
    char* password = 0;
    char decode_out[1000] = {0};
    vcd_decode_crypto(user_pass, decode_out);
    password = decode_out;

    // test
    #if 0
    host = "192.168.50.251";
    user_name = "feng";
    password = "123456";
    vmid = 102;
    node = "v-d3mcboi1";
    #endif

    // https://{host}/api/usr/login
    struct network_request_arg req = {0};
    snprintf(req.post_fields, sizeof(req.post_fields), "username=%s&password=%s", user_name, password);
    snprintf(req.url, sizeof(req.url), "https://%s/API/user/login", host);
    req.timeout = 5L;
    req.http_method = CURLOPT_POST;

    network_response* response = vcd_download_do_request_sync(&req);
    if(!response || response->response_code!=200 || !response->memory){
        M_DEBUG("[reconnect] api/login code:%d,message:%s",response->response_code,response->memory);
        return;
    }

    //M_DEBUG("[reconnect] username=%s password=%s,vimid:%d",user_name,user_pass,vmid);
    vcd_api_login_data login_data = {0};
    if(!vcd_json_parser_api_user_login(response->memory, &login_data)){
        M_ERROR("[reconnect] vcd_json_parser_api_user_login error code:%d,fail_msg:%s",login_data.code,login_data.fail_msg?login_data.fail_msg:response->memory);
        return;
    }

    free(response);

    // https://{host}/api/vm/vclient
    memset((char*)&req,0,sizeof(req));

    struct curl_slist *list = NULL;
    char token_str[500] = {0};
    sprintf(token_str,"Authorization:%s",login_data.token);
    list = curl_slist_append(list, token_str);

    snprintf(req.post_fields, sizeof(req.post_fields), "vmid=%d&node=%s", vmid, node);
    snprintf(req.url, sizeof(req.url), "https://%s/API/vm/vclient", host);
    req.timeout = 10L;
    req.http_method = CURLOPT_POST;
    req.list = list;

    //M_DEBUG("[reconnect] token_str:%s,post_fields:%s",token_str,node);
    response = vcd_download_do_request_sync(&req);
    if(!response || response->response_code!=200 || !response->memory){
        M_ERROR("[reconnect] api/vm/client failed code:%d,data:%s",response ? response->response_code : -1,response ? response->memory : " ");
        return;
    }

    vcd_api_vclient_data vvd = {0};
    if(!vcd_json_parser_api_vm_vclient(response->memory,&vvd)){
        M_ERROR("[reconnect] vcd_json_parser_api_vm_vclient error code:%d,fail_msg:%s",vvd.code,vvd.fail_msg?vvd.fail_msg:response->memory);
        return;
    }

    free(response);

    char json_str[1024] = {0};
    vcd_decode_crypto(vvd.data, json_str);

    spice_connect_data d = {0};
    if(!vcd_json_parser_spice_connect_data(json_str, &d)){
        M_ERROR("[reconnect] vcd_json_parser_spice_connect_data error:%s",d.fail_msg?d.fail_msg:json_str);
        return;
    }

    snprintf(scd->host,sizeof(scd->host),"%s",d.host);
    snprintf(scd->port,sizeof(scd->port),"%s",d.port);
    snprintf(scd->password,sizeof(scd->password),"%s",d.password);
    snprintf(scd->proxy,sizeof(scd->proxy),"%s",d.proxy);
}

/**
 * pve apitoken conf: /etc/pve/priv/token.cfg
 *                eg: root@pam!idv cce8e255-0b3e-4ef0-84a0-85cfe32ed3f7
 * @brief vcd_common_get_apitoken
 * @return cce8e255-0b3e-4ef0-84a0-85cfe32ed3f7
 */
char* vcd_common_get_apitoken(){
    char* api_token = 0;

    //GHashTable* table = g_hash_table_new(g_str_hash,g_str_equal);
    FILE* pfile = fopen(PVE_API_TOKEN_CONF,"r");
    if(pfile){
        char key[100] = {0},value[1024] = {0};

        while(fscanf(pfile,"%s%s",key,value) > 0){
            //char* pkey = strdup(key);
            //char* pvalue = strdup(value);
            //g_hash_table_insert(table,pkey,pvalue);
            if(strcmp(PVE_API_TOKEN_KEY,key) == 0){
                api_token = strdup(value);
                break;
            }
        }
        fclose(pfile);
    }
    return api_token;
}

char* vcd_common_get_unix_sock(int vmid){

    char* api_token = vcd_common_get_apitoken();
    fprintf(stderr,"apitoken:%s\n",api_token);

    char data[100] = {0};
    sprintf(data,"/var/run/qemu-server/spice-%d.sock",vmid);
    return strdup(data);
}
